package pro.cherish.smartfile.component;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import pro.cherish.smartfile.config.AppContext;
import pro.cherish.smartfile.config.properties.SysProperties;
import pro.cherish.smartfile.module.dto.TaskDto;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author 创建人：lys
 * @version 版本号：V1.0
 * <p>
 * ***************************修订记录************************************
 * 2020-12-03 lys 创建该类功能。
 * **********************************************************************
 * </p>
 * @ClassName 类名：RedisFileChunkHelper
 * @Description 功能说明：
 * <p>
 *
 * </p>
 * **********************************************************************
 * @date 创建日期： 2020-12-03 lys
 */
@Slf4j
public class RedisFileHelper {
    public static final String MERGE_FAIL_TASK_QUEUE_KEY = "smartfile:merge_fail_queue";
    private static final String REDIS_DIR_SEPARATION = ":";
    private static RedisFileHelper redisFileHelper;
    @Resource
    private SysProperties sysProperties;
    @Resource
    private RedisTemplate redisTemplate;

    public RedisFileHelper() {
        AppContext.getApplicationContext().getAutowireCapableBeanFactory().autowireBean(this);
    }

    public synchronized static RedisFileHelper getInstance() {
        if (RedisFileHelper.redisFileHelper == null) {
            return new RedisFileHelper();
        } else {
            return RedisFileHelper.redisFileHelper;
        }
    }

    /**
     * 保存任务
     *
     * @param taskDto
     */
    public void saveTaskOrUpd(TaskDto taskDto) {
        Date now = new Date();
        Date uploadTime = taskDto.getUploadTime();
        long diff = now.getTime() - uploadTime.getTime();
        long days = diff / (1000 * 60 * 60 * 24);
        long remainingDays = sysProperties.getTaskDuration() - days;
        if (remainingDays > 0) {
            // 需要构建
            String redisRoot = sysProperties.getRedisRoot();
            String key = redisRoot + REDIS_DIR_SEPARATION + taskDto.getId();
            redisTemplate.opsForValue().set(key, taskDto, remainingDays, TimeUnit.DAYS);
        }
    }

    /**
     * 获取任务
     *
     * @param taskId
     * @return
     */
    public TaskDto getTaskById(String taskId) {
        String redisRoot = sysProperties.getRedisRoot();
        String key = redisRoot + REDIS_DIR_SEPARATION + taskId;
        return (TaskDto) redisTemplate.opsForValue().get(key);
    }

    /**
     * 删除任务
     *
     * @param taskId
     * @return
     */
    public void removeTask(String taskId) {
        String redisRoot = sysProperties.getRedisRoot();
        String key = redisRoot + REDIS_DIR_SEPARATION + taskId;
        redisTemplate.delete(key);
    }

    /**
     * 清除任务
     *
     * @param taskId 任务ID
     */
    public void removeAllChunk(String taskId) {
        redisTemplate.delete(taskId);
    }

    /**
     * 消费失败的文件合并任务
     *
     * @param batchNum
     * @return
     */
    public List<TaskDto> consumeMergeFailTask(Integer batchNum) {
        ListOperations operation = redisTemplate.opsForList();
        List<TaskDto> taskDtos = Lists.newArrayList();
        for (Integer i = 0; i < batchNum; i++) {
            Object result = operation.rightPop(MERGE_FAIL_TASK_QUEUE_KEY);
            if (ObjectUtil.isNotNull(result)) {
                taskDtos.add((TaskDto) result);
            } else {
                break;
            }
        }
        return taskDtos;
    }

    /**
     * 生产合并失败任务
     *
     * @param taskDto
     */
    public void produceMergeFailTask(TaskDto taskDto) {
        ListOperations operation = redisTemplate.opsForList();
        operation.leftPush(MERGE_FAIL_TASK_QUEUE_KEY, taskDto);
    }

    /**
     * 清空任务和分片
     *
     * @param taskId
     */
    public void clearTaskAndChunks(String taskId) {
        String redisRoot = sysProperties.getRedisRoot();
        String key = redisRoot + REDIS_DIR_SEPARATION + taskId;
        redisTemplate.delete(key);
        removeAllChunk(taskId);
    }

    /**
     * 所有锁OK
     *
     * @param fileId
     * @return
     */
    public boolean checkChunkAllOk(String fileId) {
        String lockKeyLike = "chunk_lock:" + fileId + "_*";
        Set<String> allChunkLock = redisTemplate.keys(lockKeyLike);
        if (CollUtil.isEmpty(allChunkLock)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 加锁
     *
     * @param fileId
     * @param chunk
     * @return
     */
    public Long tryChunkLock(String fileId, int chunk) {
        String lockKey = "chunk_lock:" + fileId + "_" + chunk;
        Long increment = redisTemplate.opsForValue().increment(lockKey);
        redisTemplate.expire(lockKey, sysProperties.getTaskDuration(), TimeUnit.DAYS);
        return increment;
    }

    /**
     * 释放锁
     *
     * @param fileId
     * @param chunk
     */
    public void releaseChunkLock(String fileId, int chunk) {
        String lockKey = "chunk_lock:" + fileId + "_" + chunk;
        redisTemplate.delete(lockKey);
    }

    /**
     * 加锁
     *
     * @param fileId
     * @return
     */
    public Long tryMergeLock(String fileId) {
        String lockKey = "merge_lock:" + fileId + ":";
        Long increment = redisTemplate.opsForValue().increment(lockKey);
        redisTemplate.expire(lockKey, sysProperties.getTaskDuration(), TimeUnit.DAYS);
        return increment;
    }

    /**
     * 释放锁
     *
     * @param fileId
     */
    public void releaseMergeLock(String fileId) {
        String lockKey = "merge_lock:" + fileId + ":";
        redisTemplate.delete(lockKey);
    }
}
